package com.study.common.json;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.study.common.util.StringUtils;


public class JSONObject extends LinkedHashMap<String, Object> {
	private static final long serialVersionUID = 1L;
	private static final Pattern arrayNamePattern = Pattern.compile("(\\w+)((\\[\\d+\\])+)");
	private static final ObjectMapper objectMapper = new ObjectMapper();

	/**
	 * 数组结构。
	 */
	public static class JSONArray extends ArrayList<Object> {
		private static final long serialVersionUID = 1L;

		public JSONArray() {
			super();
		}

		public JSONArray(int size) {
			super(size);
		}

		@Override
		public String toString() {
			try {
				return JSON.marshal(this);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}

		@Override
		public Object set(int index, Object element) {
			return super.set(index, transfer(element));
		}

		@Override
		public boolean add(Object element) {
			return super.add(transfer(element));
		}

		@Override
		public void add(int index, Object element) {
			super.add(index, transfer(element));
		}
	}

	public JSONObject() {
		super();
	}

	public JSONObject(final JSONObject other) {
		super(other);
	}

	@Override
	public String toString() {
		try {
			return JSON.marshal(this);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 转换为紧凑格式的字符串。
	 * 
	 * @return 返回本对象紧凑格式字符串。
	 */
	public String toCompactString() {
		try {
			return objectMapper.writeValueAsString(this);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 获取指定字段的整数值。如果字段不存在，或者无法转换为整数，返回null。
	 * 
	 * @param name 字段名，支持多级。
	 * @return 返回指定的整数值，或者null。
	 */
	public Integer intValue(final String name) {
		return valueAsInt(value(name));
	}

	/**
	 * 获取指定字段的整数值。如果字段不存在，或者无法转换为整数，返回defaultValue。
	 * 
	 * @param name         字段名，支持多级。
	 * @param defaultValue 查询失败时，返回的值。
	 * @return 返回指定的整数值，或者defaultValue。
	 */
	public Integer intValue(final String name, final Integer defaultValue) {
		return StringUtils.nvl(intValue(name), defaultValue);
	}

	/**
	 * 获取指定字段的长整数值。如果字段不存在，或者无法转换为长整数，返回null。
	 * 
	 * @param name 字段名，支持多级。
	 * @return 返回指定的长整数值，或者null。
	 */
	public Long longValue(final String name) {
		return valueAsLong(value(name));
	}

	/**
	 * 获取指定字段的长整数值。如果字段不存在，或者无法转换为长整数，返回defaultValue。
	 * 
	 * @param name         字段名，支持多级。
	 * @param defaultValue 查询失败时，返回的值。
	 * @return 返回指定的长整数值，或者defaultValue。
	 */
	public Long longValue(final String name, final Long defaultValue) {
		return StringUtils.nvl(longValue(name), defaultValue);
	}

	/**
	 * 获取指定字段的布尔值。如果字段不存在，或者无法转换为布尔型，返回null。
	 * 
	 * @param name 字段名，支持多级。
	 * @return 返回指定的布尔值，或者null。
	 */
	public Boolean boolValue(final String name) {
		return valueAsBool(value(name));
	}

	/**
	 * 获取指定字段的布尔值。如果字段不存在，或者无法转换为布尔型，返回defaultValue。
	 * 
	 * @param name         字段名，支持多级。
	 * @param defaultValue 查询失败时，返回的值。
	 * @return 返回指定的布尔值，或者defaultValue。
	 */
	public Boolean boolValue(final String name, final Boolean defaultValue) {
		return StringUtils.nvl(boolValue(name), defaultValue);
	}

	/**
	 * 获取指定字段的字符串值。如果字段不存在，返回null。
	 * 
	 * @param name 字段名，支持多级。
	 * @return 返回指定的字符串值，或者null。
	 */
	public String strValue(final String name) {
		return valueAsStr(value(name));
	}

	/**
	 * 获取指定字段的字符串值。如果字段不存在，返回defaultValue。
	 * 
	 * @param name         字段名，支持多级。
	 * @param defaultValue 查询失败时，返回的值。
	 * @return 返回指定的字符串值，或者defaultValue。
	 */
	public String strValue(final String name, final String defaultValue) {
		return StringUtils.nvl(strValue(name), defaultValue);
	}

	/**
	 * 获取指定字段的值。
	 * 
	 * @param name 字段名，支持多级，支持数组下标。
	 * @return 返回指定字段的值。
	 */
	public Object value(final String name) {
		final int indexDot = name.indexOf('.');
		if (indexDot >= 0) {
			return obj(name.substring(0, indexDot)).value(name.substring(indexDot + 1));
		} else {
			final Matcher matcher = arrayNamePattern.matcher(name);
			if (matcher.find()) {
				return endArray(matcher.group(1), matcher.group(2), new EndArrayCallback<Object>() {
					@Override
					public Object callback(JSONArray arr, int index) {
						return elementAt(arr, index);
					}
				});
			} else {
				return get(name);
			}
		}
	}

	/**
	 * 设置指定字段的值。
	 * 
	 * @param name  字段名，支持多级，支持数组下标。
	 * @param value 字段值。
	 * @return 返回本对象。
	 */
	public JSONObject value(final String name, final Object value) {
		final int indexDot = name.indexOf('.');
		if (indexDot >= 0) {
			obj(name.substring(0, indexDot)).value(name.substring(indexDot + 1), value);
		} else {
			final Matcher matcher = arrayNamePattern.matcher(name);
			if (matcher.find()) {
				endArray(matcher.group(1), matcher.group(2), new EndArrayCallback<Void>() {
					@Override
					public Void callback(JSONArray arr, int index) {
						elementAt(arr, index, value);
						return null;
					}
				});
			} else {
				set(name, value);
			}
		}
		return this;
	}

	/**
	 * 获取对象（非标量类型）字段。返回的数据是一个结构体。当不存在指定对象时，则为指定的名字创建一个空的MessageObject对象。
	 * 
	 * @param name 字段名。不支持多级名字，支持数组下标。
	 * @return 返回指定的对象。如果对象不存在，则为指定的名字创建一个空的MessageObject对象。
	 */
	public JSONObject obj(final String name) {
		final Matcher matcher = arrayNamePattern.matcher(name);
		if (matcher.find()) {
			return endArray(matcher.group(1), matcher.group(2), new EndArrayCallback<JSONObject>() {
				@Override
				public JSONObject callback(JSONArray arr, int index) {
					return objAt(arr, index);
				}
			});
		} else {
			JSONObject obj = getObj(name);
			if (obj == null) {
				obj = new JSONObject();
				put(name, obj);
			}
			return obj;
		}
	}

	/**
	 * 获取数组字段。将名字对应的对象以数组对象返回，当指定的字段不存在时，创建一个空的数组。
	 * 
	 * @param name 字段名。不支持多级名字，不支持下标。
	 * @return 返回一个数组（List）。
	 */
	public JSONArray arr(final String name) {
		JSONArray arr = getArr(name);
		if (arr == null) {
			arr = new JSONArray();
			put(name, arr);
		}
		return arr;
	}

	/**
	 * 获取对象（非标量类型）字段。返回的数据是一个结构体。
	 * 
	 * @param name 字段名。
	 * @return 返回指定的对象字段。
	 */
	public JSONObject getObj(final String name) {
		return (JSONObject) get(name);
	}

	/**
	 * 获取数组类型字段。
	 * 
	 * @param name 字段名。
	 * @return 返回数组类型字段。
	 */
	public JSONArray getArr(final String name) {
		return (JSONArray) get(name);
	}

	/**
	 * 返回字段整数值。如果不存在，返回null。
	 * 
	 * @param name 字段名。
	 * @return 返回指定字段整数值。
	 */
	public Integer getInt(final String name) {
		return valueAsInt(get(name));
	}

	/**
	 * 返回字段整数值。如果不存在，返回defaultValue。
	 * 
	 * @param name         字段名。
	 * @param defaultValue 字段不存在时，返回的值。
	 * @return 返回指定字段整数值。
	 */
	public Integer getInt(final String name, Integer defaultValue) {
		return StringUtils.nvl(getInt(name), defaultValue);
	}

	/**
	 * 返回字段长整数值。如果不存在，返回null。
	 * 
	 * @param name 字段名。
	 * @return 返回指定字段长整数值。
	 */
	public Long getLong(final String name) {
		return valueAsLong(get(name));
	}

	/**
	 * 返回字段长整数值。如果不存在，返回defaultValue。
	 * 
	 * @param name         字段名。
	 * @param defaultValue 字段不存在时，返回的值。
	 * @return 返回指定字段长整数值。
	 */
	public Long getLong(final String name, Long defaultValue) {
		return StringUtils.nvl(getLong(name), defaultValue);
	}

	/**
	 * 返回字段字符串值。如果不存在，返回null。
	 * 
	 * @param name 字段名。
	 * @return 返回指定字段字符串值。
	 */
	public String getStr(final String name) {
		return valueAsStr(get(name));
	}

	/**
	 * 返回字段字符串值。如果不存在，返回defaultValue。
	 * 
	 * @param name         字段名。
	 * @param defaultValue 字段不存在时，返回的值。
	 * @return 返回指定字段字符串值。
	 */
	public String getStr(final String name, final String defaultValue) {
		return StringUtils.nvl(getStr(name), defaultValue);
	}

	/**
	 * 字段值按照布尔类型返回。如果不存在，返回null。
	 * 
	 * @param name 字段名。
	 * @return 字段值。
	 */
	public Boolean getBool(final String name) {
		return valueAsBool(get(name));
	}

	/**
	 * 字段值按照布尔类型返回。如果不存在，返回defaultValue。
	 * 
	 * @param name         字段名。
	 * @param defaultValue 字段不存在时，返回的值。
	 * @return 字段值。
	 */
	public Boolean getBool(final String name, final Boolean defaultValue) {
		return StringUtils.nvl(getBool(name), defaultValue);
	}

	/**
	 * 设置字段值
	 * 
	 * @param name  字段名
	 * @param value 字段值（标量：数字、字符串、布尔型；结构体：MessageObject）。
	 *              如果是Map类型同时非MessageObject类型，则自动转换为MessageObject类型再存入
	 *              （此时，再修改Map中的数据，将不会体现到本对象中）。
	 * @return 返回本对象
	 */
	public JSONObject set(final String name, final Object value) {
		put(name, value);
		return this;
	}

	/**
	 * 将本对象转换为Java Bean。
	 * 
	 * @param beanClass Java Bean的类对象。
	 * @return 返回转换后的Java Bean。
	 */
	public <T> T asBean(Class<T> beanClass) {
		try {
			return JSON.unmarshal(JSON.marshal(this), beanClass);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 重载基类的方法。如果 value 是 Map 类型，但不是 MessageObject 类型，则创建一个包含内容等同于原 Map 的
	 * MessageObject 作为 value（注意：此后再更改 Map 的内容，将不会反映到 MessageObject 中）。
	 * 重载此方法的目的是为了使JSON能够正确地解析为MessageObject对象。不建议直接调用此方法，请使用 set(name,
	 * value)方法设置字段值。
	 */
	@Override
	public Object put(String key, Object value) {
		return super.put(key, transfer(value));
	}

	public static Integer valueAsInt(Object value) {
		if (value instanceof Integer) {
			return (Integer) value;
		} else if (value instanceof Number) {
			return ((Number) value).intValue();
		} else if (value instanceof String) {
			return Integer.valueOf((String) value);
		} else if (value instanceof Boolean) {
			return ((Boolean) value) ? 1 : 0;
		} else {
			return null;
		}
	}

	public static Long valueAsLong(Object value) {
		if (value instanceof Long) {
			return (Long) value;
		} else if (value instanceof Number) {
			return ((Number) value).longValue();
		} else if (value instanceof String) {
			return Long.valueOf((String) value);
		} else if (value instanceof Boolean) {
			return ((Boolean) value) ? 1L : 0L;
		} else {
			return null;
		}
	}

	public static String valueAsStr(Object value) {
		if (value instanceof String) {
			return (String) value;
		} else if (value != null) {
			return value.toString();
		} else {
			return null;
		}
	}

	public static Boolean valueAsBool(Object value) {
		if (value instanceof Boolean) {
			return (Boolean) value;
		} else if (value instanceof Number) {
			return ((Number) value).doubleValue() != 0.0;
		} else if (value instanceof String) {
			return Boolean.valueOf((String) value);
		} else {
			return null;
		}
	}

	/**
	 * 将所有层次中凡是Map类型同时又不是MessageObject的类型，转换为MessageObject类型。
	 * 
	 * @param value 值。
	 * @return 返回转换后的值。
	 */
	@SuppressWarnings("unchecked")
	private static Object transfer(final Object value) {
		if (!(value instanceof JSONObject) && value instanceof Map) {
			return toObj((Map<String, Object>) value);
		} else if (!(value instanceof JSONArray) && value instanceof Collection) {
			return toArr((Collection<Object>) value);
		} else {
			return value;
		}
	}

	private static JSONArray toArr(final Collection<Object> list) {
		final JSONArray arr = new JSONArray(list.size());
		for (final Object element : list) {
			arr.add(element);
		}
		return arr;
	}

	private static JSONObject toObj(final Map<String, Object> map) {
		final JSONObject obj = new JSONObject();
		for (final Map.Entry<String, Object> ent : map.entrySet()) {
			obj.put(ent.getKey(), transfer(ent.getValue()));
		}
		return obj;
	}

	/**
	 * 将指定下标元素作为数组返回，如果不存在，则在该位置创建一个空的数组。
	 * 
	 * @param arr   当前数组。
	 * @param index 下标。
	 * @return 返回当前数组指定下标的元素，该元素应该是一个数组。
	 */
	private static JSONArray arrayAt(JSONArray arr, int index) {
		expand(arr, index);
		if (arr.get(index) == null) {
			arr.set(index, new JSONArray());
		}
		return (JSONArray) arr.get(index);
	}

	/**
	 * 将指定下标元素作为结构体返回，如果不存在，则在该位置创建一个空的结构体。
	 * 
	 * @param arr   当前数组。
	 * @param index 下标。
	 * @return 返回当前数组指定下标元素，该元素是一个结构体。
	 */
	private static JSONObject objAt(final JSONArray arr, int index) {
		expand(arr, index);
		if (arr.get(index) == null) {
			arr.set(index, new JSONObject());
		}
		return (JSONObject) arr.get(index);
	}

	/**
	 * 设置数组指定下标位置的值。
	 * 
	 * @param arr   数组。
	 * @param index 下标。
	 * @param value 值。
	 */
	private static void elementAt(final JSONArray arr, final int index, final Object value) {
		expand(arr, index).set(index, value);
	}

	/**
	 * 获取数组指定下标元素的值。
	 * 
	 * @param arr   数组。
	 * @param index 下标。
	 * @return 值。
	 */
	private static Object elementAt(final JSONArray arr, final int index) {
		return expand(arr, index).get(index);
	}

	/**
	 * 扩展数组到指定下标，以防止访问时下标越界。
	 * 
	 * @param arr   数组
	 * @param index 下标
	 * @return 返回传入的数组
	 */
	private static JSONArray expand(final JSONArray arr, final int index) {
		while (arr.size() <= index) {
			arr.add(null);
		}
		return arr;
	}

	/**
	 * 最后数组回调。
	 * 
	 * @author Mike
	 *
	 * @param <T> 回调返回数据类型。
	 */
	private interface EndArrayCallback<T> {
		/**
		 * 当定位到最后一级数组，将调用本方法。
		 * 
		 * @param arr   最后一级数组对象。
		 * @param index 最后一级索引。
		 * @return 返回回调的返回值。
		 */
		T callback(JSONArray arr, int index);
	}

	/**
	 * 处理多维数组的工具函数（包括一维数组）。多维数组的名字如：arrary[1][2][3]，
	 * 则name=array，indexStr=[1][2][3]，在callback中，endArr将是 array[1][2]指定的对象，indexe=3。
	 * 
	 * @param name       不带下标的名字，不支持多级名字。
	 * @param indexesStr 索引部分的字符串，如：[1][2][3]
	 * @param callback   回调函数。
	 * @return 返回回调函数的返回值。
	 */
	private <T> T endArray(final String name, final String indexesStr, final EndArrayCallback<T> callback) {
		JSONArray endArr = arr(name);
		final int[] indexes = parseIndexes(indexesStr);
		int i = 0;
		while (i < indexes.length - 1) {
			endArr = arrayAt(endArr, indexes[i++]);
		}
		return callback.callback(endArr, indexes[i]);
	}

	private static int[] parseIndexes(final String s) {
		int[] indexes = null;
		List<Integer> list = new ArrayList<Integer>();

		final StringTokenizer st = new StringTokenizer(s, "[]");
		while (st.hasMoreTokens()) {
			final int index = Integer.valueOf(st.nextToken());
			if (index < 0) {
				throw new RuntimeException(String.format("Illegal index %1$d in \"%2$s\"", index, s));
			}

			list.add(index);
		}

		indexes = new int[list.size()];
		int i = 0;
		for (Integer tmp : list.toArray(new Integer[list.size()])) {
			indexes[i++] = tmp;
		}

		return indexes;
	}
}
